#include "stdio.h"
#include "large_int.h"
#include "string.h"

bool largeIntParse(sLargeInt_t *largeIntPtr, const char *intStr)
{
	char tempChar = '\0';
	int i = 0, end = 0;
	
	if (!largeIntPtr || !intStr)
		return false;
		
	// Get string len
	uint32_t strLen = strlen(intStr);

	// Init, set the large int len to 0.
	largeIntPtr->len = 0;
	
	// Check if positive
	tempChar = intStr[0];
	if (tempChar == '-')
	{
		largeIntPtr->minus = true;
		end = 1;
	}
	else
	{
		largeIntPtr->minus = false;
	}
	
	// parse the string to large int
	for (i = strLen - 1; i >= end; i--)
	{
		tempChar = intStr[i];
		if (tempChar >= '0' && tempChar <= '9')
		{
			largeIntPtr->data[largeIntPtr->len++] = tempChar - '0';
		}
		else
		{
			largeIntPtr->data[largeIntPtr->len++] = 0;
		}
	}

	if (largeIntPtr->len == 0)
	{
		largeIntPtr->len = 1;
		largeIntPtr->data[0] = 0;
	}
}

static bool largeIntIsPosLargerOrEqual(sLargeInt_t *base, sLargeInt_t *comparer)
{
	if (base->len > comparer->len)
	{
		return true;
	}
	else if (base->len == comparer->len)
	{
		uint16_t i = base->len - 1;
		for (; i >= 0; i--)
		{
			if (base->data[i] == comparer->data[i])
			{
				continue;
			}
			else
			{
				return base->data[i] > comparer->data[i];
			}
		}
		return true;
	}

	// base->len < comparer->len
	return false;
}

bool largeIntIsLargerOrEqual(sLargeInt_t *base, sLargeInt_t *comparer)
{
	if (!base->minus)
	{
		if (comparer->minus)
		{
			return true;
		}
		else
		{
			return largeIntIsPosLargerOrEqual(base, comparer);
		}
	}
	else
	{
		if (!comparer->minus)
		{
			return false;
		}
		else
		{
			return !largeIntIsPosLargerOrEqual(base, comparer);
		}
	}
	return true;
}

static void largeIntPosAdd(sLargeInt_t *addend, sLargeInt_t *summand, sLargeInt_t *result)
{
	uint8_t over_flow = 0;
	uint16_t add_max_len = MAX(summand->len, addend->len);
	uint16_t add_min_len = MIN(summand->len, addend->len);
	sLargeInt_t *longerDataPtr = addend->len > summand->len? addend: summand;
	uint16_t i = 0;
	uint8_t sum = 0;

	result->minus = false;

	for (i=0; i<add_min_len; i++)
	{
		sum = addend->data[i] + summand->data[i] + over_flow;
		result->data[i] = sum % 10;
		// actually, the over flow should less or equal to 1.
		over_flow = sum / 10;
	}
	
	for (i=add_min_len; i<add_max_len; i++)
	{
		sum = longerDataPtr->data[i] + over_flow;
		result->data[i] = sum % 10;
		// actually, the over flow should less or equal to 1.
		over_flow = sum / 10;
	}
	
	if (over_flow)
	{
		result->data[add_max_len] = over_flow;
		result->len = add_max_len + 1;
	}
	else
	{
		result->len = add_max_len;
	}
}

static void largeIntPosSub(sLargeInt_t *subtrahend, sLargeInt_t *minuend, sLargeInt_t *result)
{
	uint8_t over_borrow = 0;
	uint16_t i = 0;
	uint8_t curMinuend = 0;

	result->minus = false;

	for (i=0; i<minuend->len; i++)
	{
		curMinuend = over_borrow + minuend->data[i];
		if (curMinuend > subtrahend->data[i])
		{
			over_borrow = 1;
			result->data[i] = 10 - curMinuend + subtrahend->data[i];
		}
		else
		{
			over_borrow = 0;
			result->data[i] = subtrahend->data[i] - curMinuend;
		}
	}
	
	uint16_t index = minuend->len;
	
	// the api should be used when subtrahend is pos larger than minuend
	while (over_borrow > 0 && index < subtrahend->len)
	{
		if (subtrahend->data[index] >= 1)
		{
			over_borrow = 0;
			result->data[index] = subtrahend->data[index] - 1;
		}
		else
		{
			over_borrow = 1;
			result->data[index] = 9;
		}
		index++;
	}
	
	// sub len by 1 if the largest data is 0
	index = subtrahend->len;

	while(result->data[index] == 0 && index >= 0)
	{
		index--;
	}
	
	result->len = index + 1;

	if (result->len = 0)
	{
		result->len = 1;
		result->data[0] = 0;
	}
}

void largeIntAdd(sLargeInt_t *addend, sLargeInt_t *summand, sLargeInt_t *result)
{
	if (!addend->minus && !summand->minus)
	{
		return largeIntPosAdd(addend, summand, result);
	}
	else if (!addend->minus && summand->minus)
	{
		if (largeIntIsPosLargerOrEqual(addend, summand))
		{
			return largeIntPosSub(addend, summand, result);
		}
		else
		{
			largeIntPosSub(summand, addend, result);
			result->minus = true;
			return;
		}
	}
	else if (addend->minus && !summand->minus)
	{
		if (largeIntIsPosLargerOrEqual(addend, summand))
		{
			largeIntPosSub(addend, summand, result);
			result->minus = true;
			return;
		}
		else
		{
			return largeIntPosSub(summand, addend, result);
		}
	}
	else // both negitive
	{
		largeIntPosAdd(addend, summand, result);
		result->minus = true;
		return;
	}
}

void largeIntSub(sLargeInt_t *subtrahend, sLargeInt_t *minuend, sLargeInt_t *result)
{
	if (!subtrahend->minus && !minuend->minus)
	{
		if (largeIntIsPosLargerOrEqual(subtrahend, minuend))
		{
			return largeIntPosSub(subtrahend, minuend, result);
		}
		else 
		{
			largeIntPosSub(minuend, subtrahend, result);
			result->minus = true;
			return;
		}
	}
	else if (!subtrahend->minus && minuend->minus)
	{
		return largeIntPosAdd(subtrahend, minuend, result);
	}
	else if (subtrahend->minus && !minuend->minus)
	{
		largeIntPosAdd(subtrahend, minuend, result);
		result->minus = true;
		return;
	}
	else // both negitive
	{
		if (largeIntIsPosLargerOrEqual(minuend, subtrahend))
		{
			return largeIntPosSub(minuend, subtrahend, result);
		}
		else
		{
			largeIntPosSub(subtrahend, minuend, result);
			result->minus = true;
			return;
		}
	}
}

static void largeIntSetZero(sLargeInt_t *result)
{
	result->minus = false;
	result->data[0] = 0;
	result->len = 1;
}

static void largeIntMulOne(sLargeInt_t *multiplicator, uint8_t multiplicand, sLargeInt_t *result)
{
	uint8_t over_flow = 0, multResult;
	uint8_t i = 0;
	for (i = 0; i < multiplicator->len; i++)
	{
		multResult = over_flow + multiplicator->data[i] * multiplicand;
		result->data[i] = multResult % 10;
		// in fact, the over flow must smaller than 10
		over_flow = multResult / 10;
	}

	if (over_flow)
	{
		result->data[multiplicator->len] = over_flow;
		result->len = multiplicator->len + 1;
	}
	else
	{
		result->len = multiplicator->len;
	}
}

static void largeIntPosMul(sLargeInt_t *multiplicator, sLargeInt_t *multiplicand, sLargeInt_t *result)
{
	sLargeInt_t mulOneResult;
	int i = multiplicand->len - 1;

	largeIntSetZero(result);

	if (largeIntIsZero(multiplicator) || largeIntIsZero(multiplicand))
	{
		return;
	}

	for (; i >= 0; i--)
	{
		largeIntMulOne(multiplicator, multiplicand->data[i], &mulOneResult);
		largeIntAdd(&mulOneResult, result, result);
		if (i > 0)
		{
			largeIntMulOne(result, 10, result);
		}
	}
}

void largeIntMul(sLargeInt_t *multiplicator, sLargeInt_t *multiplicand, sLargeInt_t *result)
{
	largeIntPosMul(multiplicator, multiplicand, result);
	result->minus = multiplicator->minus != multiplicand->minus;
}

void largeIntPrint(sLargeInt_t *largeIntPtr)
{
	if (!largeIntPtr)
	{
		fprintf(stdout, "largeIntPrint(), large int pointer is NULL\r\n");
	}
	else if (largeIntPtr->len == 0)
	{
		fprintf(stdout, "largeIntPrint(), large int len is 0\r\n");
	}
	else 
	{
		// fprintf(stdout, "largeIntPrint() \r\n");
		fprintf(stdout, "\t");
		if (largeIntPtr->minus)
		{
			fprintf(stdout, "-");
		}
		int i = largeIntPtr->len - 1;
		for (;  i >= 0; i--)
		{
			fprintf(stdout, "%d", largeIntPtr->data[i]);
		}
		fprintf(stdout, "\r\n");
	}
}

bool largeIntIsZero(sLargeInt_t *base)
{
	if (!base)
		return true;
	if (base->len == 0)
		return true;
	if (base->len == 1 && base->data[0] == 0)
		return true;
	return false;
}